package biz

import (
	"context"
	"github.com/go-kratos/kratos/v2/log"
	adminMenuApi "go-skeleton/api/admin/service/admin_menu"
	"go-skeleton/app/admin/service/internal/data/ent"
	"google.golang.org/protobuf/types/known/emptypb"
)

type AdminMenuRepo interface {
	Save(context.Context, *ent.AdminMenu) (*ent.AdminMenu, error)
	Update(context.Context, *ent.AdminMenu) (*ent.AdminMenu, error)
	FindByID(context.Context, int64) (*ent.AdminMenu, error)
	List(context.Context, []string) ([]*ent.AdminMenu, error)
	ListAll(context.Context) ([]*ent.AdminMenu, error)
	Del(context.Context, int64) error
}

type AdminMenuBiz struct {
	repo AdminMenuRepo
	log  *log.Helper
}

func NewAdminMenuBiz(repo AdminMenuRepo, logger log.Logger) *AdminMenuBiz {
	return &AdminMenuBiz{repo: repo, log: log.NewHelper(log.With(logger, "module", "biz/admin/menu"))}
}

func (b *AdminMenuBiz) Save(ctx context.Context, req *adminMenuApi.MenuInfo) (resp *adminMenuApi.MenuInfo, err error) {
	saveMenu, err := b.repo.Save(ctx, b.protoToEnt(req))
	if err != nil {
		return
	}
	resp = b.entToProto(saveMenu)

	return
}

func (b *AdminMenuBiz) Update(ctx context.Context, req *adminMenuApi.MenuInfo) (resp *adminMenuApi.MenuInfo, err error) {
	updateMenu, err := b.repo.Update(ctx, b.protoToEnt(req))
	if err != nil {
		return
	}
	resp = b.entToProto(updateMenu)

	return
}

func (b *AdminMenuBiz) FindByID(ctx context.Context, id int64) (*ent.AdminMenu, error) {
	return b.repo.FindByID(ctx, id)
}

func (b *AdminMenuBiz) GetMenu(ctx context.Context, names []string) (resp *adminMenuApi.GetMenuReply, err error) {
	resp = &adminMenuApi.GetMenuReply{
		Data: make([]*adminMenuApi.MenuInfo, 0),
	}

	list, err := b.repo.List(ctx, names)
	if err != nil {
		return
	}
	resp.Data = b.buildTree(list, 0)
	return
}

func (b *AdminMenuBiz) GetMenuAll(ctx context.Context) (resp *adminMenuApi.GetMenuReply, err error) {
	resp = &adminMenuApi.GetMenuReply{
		Data: make([]*adminMenuApi.MenuInfo, 0),
	}

	list, err := b.repo.ListAll(ctx)
	if err != nil {
		return
	}
	resp.Data = b.buildTree(list, 0)
	return
}

func (b *AdminMenuBiz) buildTree(nodes []*ent.AdminMenu, root int64) []*adminMenuApi.MenuInfo {
	menuMap := make(map[int64]*adminMenuApi.MenuInfo, len(nodes))

	for _, menu := range nodes {
		menuMap[menu.ID] = b.entToProto(menu)
	}

	var result []*adminMenuApi.MenuInfo
	for _, m := range nodes {
		if m.ParentID == root {
			result = append(result, menuMap[m.ID])
		} else {
			menuMap[m.ParentID].Children = append(menuMap[m.ParentID].Children, menuMap[m.ID])
		}
	}

	return result
}

func (b *AdminMenuBiz) entToProto(menu *ent.AdminMenu) *adminMenuApi.MenuInfo {
	return &adminMenuApi.MenuInfo{
		Id:         menu.ID,
		MenuType:   menu.MenuType,
		ParentId:   menu.ParentID,
		ParentPath: menu.ParentPath,
		Sort:       menu.Sort,
		Path:       menu.Path,
		Name:       menu.Name,
		Component:  menu.Component,
		Redirect:   menu.Redirect,
		Meta: &adminMenuApi.MenuMeta{
			Title:       menu.Title,
			IsLink:      menu.IsLink,
			IsHide:      menu.IsHide,
			IsKeepAlive: menu.IsKeepAlive,
			IsAffix:     menu.IsAffix,
			IsIframe:    menu.IsIframe,
			Icon:        menu.Icon,
		},
		Children: []*adminMenuApi.MenuInfo{},
	}
}

func (b *AdminMenuBiz) protoToEnt(menu *adminMenuApi.MenuInfo) *ent.AdminMenu {
	return &ent.AdminMenu{
		ID:          menu.Id,
		MenuType:    menu.MenuType,
		ParentID:    menu.ParentId,
		ParentPath:  menu.ParentPath,
		Sort:        menu.Sort,
		Path:        menu.Path,
		Name:        menu.Name,
		Component:   menu.Component,
		Redirect:    menu.Redirect,
		Title:       menu.Meta.Title,
		IsLink:      menu.Meta.IsLink,
		IsHide:      menu.Meta.IsHide,
		IsKeepAlive: menu.Meta.IsKeepAlive,
		IsAffix:     menu.Meta.IsAffix,
		IsIframe:    menu.Meta.IsIframe,
		Icon:        menu.Meta.Icon,
	}
}

func (b *AdminMenuBiz) DelMenu(ctx context.Context, id int64) (resp *emptypb.Empty, err error) {
	err = b.repo.Del(ctx, id)

	return
}
